#pragma once

#include <ddblk/apds/combine_int.h>
#include <dbg.h>

typedef struct _dsmsyncnode
{
	APDSFUTURE_DECL
	struct _dsmsyncnode *next;
} _dsmsyncnode_t;

typedef struct _dsmsync
{
	_dsmsyncnode_t *tail;
} _dsmsync_t;

DECL_COMBINE(dsmsync)

SAI void __dsmsync_init(_dsmsync_t *s)
{
	s->tail = NULL;
}
SAI void *__dsmsync_destruct(_dsmsync_t *s)
{
	ASSERT(s->tail == NULL);
	return (void *)s;
}

SAI _dsmsyncnode_t *__dsmsync_fetch(_dsmsync_t *s, apdsfunc_t *pfunc, apdsarg_t *args, _dsmsyncnode_t *node){
	PARAM_USED(s);
	apds_movargs(&node->args, args);
	node->func = pfunc;

	/* 确保已经清空 */
	node->flag = COMBINE_FLG_WAIT;
	node->next = NULL;
	/* cmb(); */
	return node;
}
SAI apdsret_t __dsmsync_wait(_dsmsync_t *s, _dsmsyncnode_t *node, INFUN(cmbn_wait, waitpolicy)){
	waitcnt_t cnt = -1;
	_dsmsyncnode_t *cur_node, *next_node;
	lkword_t flag_wait = COMBINE_FLG_WAIT;
	word_t help_count = 0;
	lkword_t flag;
	_dsmsyncnode_t *my_node, *my_pred_node;
	my_node = node;
	my_pred_node = swp_rel(&s->tail, my_node);
	if (my_pred_node)
	{
		atm_st(&my_pred_node->next, my_node);
		flag = atm_ld(&my_node->flag);
		while (flag == COMBINE_FLG_WAIT)
		{
			cnt = waitpolicy->waitf(&my_node->flag, &flag_wait, cnt);
			flag = atm_ld(&my_node->flag);
		}
		if (flag == COMBINE_FLG_COMPLETED)
		{
			mp_rmb(); /* 同一cache行且存在读依赖 */
			return apds_args_ret(&my_node->args);
		}
	}
	mp_rmb();


	#if 0
	tmp_node = my_node; 
	tmp_node_next = atm_ld(&tmp_node->next);
	
	do
	{

		APDS_PREFETCH(tmp_node_next);

		apds_set_ret(&tmp_node->args, apds_sync_single(tmp_node->func, &tmp_node->args));
		// atm_st(&tmp_node->args.arg[0], apds_sync_single(tmp_node->args.func, &tmp_node->args));
		atm_strel(&tmp_node->flag, COMBINE_FLG_COMPLETED);


		if (++help_count >= APDS_HELP_QUOTA || !tmp_node_next)
			break;
		dsmsyncnode_t *tmp = atm_ld(&tmp_node_next->next);
		if (!tmp) break;
		tmp_node = tmp_node_next;
		tmp_node_next = tmp;
	} while (tmp_node_next);
	#endif

	/* 为什么要限制0 -> 1 -> 2, 是因为有memory reclaim的问题 */
	cur_node = my_node; 
	do
	{
		/* 帮助 */
		/* cmb(); */
		apds_set_ret(&cur_node->args, apds_sync_single(cur_node->func, &cur_node->args));
		/* cmb(); */
		next_node = atm_ld(&cur_node->next);
		/* APDS_PREFETCH(next_node); */


		/* 释放, 确保写操作都不会被重排到后方 */
		atm_strel(&cur_node->flag, COMBINE_FLG_COMPLETED);
		if (++help_count >= APDS_HELP_QUOTA || !next_node || !next_node->next) break;
		cur_node = next_node;
		/* 下个节点不为空 */
	} while (TRUE);

	/* apdsret_t ret = apds_args_ret(&my_node->args); */
	if (!next_node)
	{
		_dsmsyncnode_t *oldval = cur_node;
		if (cas_rel_s(&s->tail, &oldval, NULL))
		{
			return apds_args_ret(&my_node->args);
		}
		next_node = atm_ld(&cur_node->next);
		while (!next_node)
		{
			PAUSE();
			next_node = atm_ld(&cur_node->next);
		}
	}
	// 处理尾部
	atm_strel(&next_node->flag, COMBINE_FLG_OWNED);
	/* cmb(); */
	
	lkword_t flag_owned = COMBINE_FLG_OWNED;
	waitpolicy->wakef(&next_node->flag, &flag_owned);
	return apds_args_ret(&my_node->args);
}

PARAM_NONULL(1) SAI _dsmsync_t *_dsmsync_init(void *ptr, size_t size, INFUN(cmbn_node, nodemgr))
{
	PARAM_USED(nodemgr);
	if (size < sizeof(_dsmsync_t)) return NULL;
	_dsmsync_t *s = (_dsmsync_t *)ptr;
	__dsmsync_init(s);
	return s;
}
PARAM_NONULL(1) SAI void *_dsmsync_destruct(_dsmsync_t *s, INFUN(cmbn_node, nodemgr)){
	PARAM_USED(nodemgr);
	void *rptr = __dsmsync_destruct(s);
	return rptr;
}
PARAM_NONULL(1, 2, 3) SAI _dsmsyncnode_t *_dsmsync_fetch(_dsmsync_t *s, apdsfunc_t *pfunc, apdsarg_t *args, apdstinfo_t *tinfo, INFUN(cmbn_node, nodemgr)){
	PARAM_USED(tinfo);
	_dsmsyncnode_t *node = (_dsmsyncnode_t *)nodemgr->get_lknode(sizeof(_dsmsyncnode_t));
	return __dsmsync_fetch(s, pfunc, args, node);
}
PARAM_NONULL(1, 2) SAI apdsret_t _dsmsync_wait(_dsmsync_t *s, _dsmsyncnode_t *node, apdstinfo_t *tinfo, INFUN(cmbn_wait, waitpolicy), INFUN(cmbn_node, nodemgr)){
	PARAM_USED(tinfo);
	apdsret_t ret = __dsmsync_wait(s, node, waitpolicy);
	nodemgr->ret_lknode(node, sizeof(_dsmsyncnode_t));
	return ret;
}
